// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef TOOLS_CLANG_PLUGINS_UTIL_H_
#define TOOLS_CLANG_PLUGINS_UTIL_H_

#include <string>
#include <type_traits>

#include "clang/AST/DeclBase.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/HeaderSearchOptions.h"

// Utility method for subclasses to determine the namespace of the
// specified record, if any. Unnamed namespaces will be identified as
// "<anonymous namespace>".
std::string GetNamespace(const clang::Decl* record);

enum class FilenamesFollowPresumed {
  kYes,
  kNo,
};

enum class FilenameLocationType {
  // Use the location as is.
  kExactLoc,
  // Use the spelling location of the current location. This leaves a macro if
  // the token at the location came from a macro parameter.
  kSpellingLoc,
  // Use the expansion location of the current location. This always leaves a
  // macro.
  kExpansionLoc,
};

// Attempts to determine the filename for the given SourceLocation. Returns an
// empty string if the filename could not be determined.
//
// Most callers use FilenameLocationType::kSpellingLoc which gets the filename
// where a macro is used, if the name at the location is coming from a macro
// parameter. This would be used when a warning is being generated based on the
// macro inputs, rather than the macro's structure itself. It may be an
// incorrect approximation of kExpansionLoc, though.
std::string GetFilename(
    const clang::SourceManager& instance,
    clang::SourceLocation location,
    FilenameLocationType type,
    FilenamesFollowPresumed follow_presumed = FilenamesFollowPresumed::kYes);

// Utility method to obtain a "representative" source location polymorphically.
// We sometimes use a source location to determine a code owner has legitimate
// justification not to fix the issue found out by the plugin (e.g. the issue
// being inside system headers). Among several options to obtain a location,
// this utility aims to provide the best location which represents the node's
// essential token.
inline clang::SourceLocation getRepresentativeLocation(
    const clang::Stmt& node) {
  // clang::Stmt has T::getBeginLoc() and T::getEndLoc().
  // Usually the former one does better represent the location.
  //
  // e.g. clang::IfStmt
  // if (foo) {} else {}
  // ^                 ^
  // |                 getEndLoc()
  // getBeginLoc()
  //
  // e.g. clang::CastExpr
  // int x = static_cast<int>(123ll);
  //         ^                     ^
  //         |                     getEndLoc()
  //         getBeginLoc()
  return node.getBeginLoc();
}
inline clang::SourceLocation getRepresentativeLocation(
    const clang::TypeLoc& node) {
  // clang::TypeLoc has T::getBeginLoc() and T::getEndLoc().
  // As the former may refer to modifiers, we use the latter one.
  return node.getEndLoc();
}
inline clang::SourceLocation getRepresentativeLocation(
    const clang::Decl& node) {
  // Unlike other nodes, clang::Decl provides T::getLocation().
  // Usually, this provides more "representative" location.
  //
  // e.g. clang::FieldDecl
  //   int* field = nullptr;
  //   ^    ^       ^
  //   |    |       getEndLoc()
  //   |    getLocation()
  //   getBeginLoc()
  return node.getLocation();
}

namespace chrome_checker {

enum LocationClassification {
  // First-party Chromium code that is part of the main project repo.
  kFirstParty,
  // Blink is first-party but is treated differently sometimes, with different
  // style rules.
  kBlink,
  // Third-party code that is owned by the Chromium project.
  kChromiumThirdParty,
  // Third-party code that is not owned by the Chromium project, imported from
  // external projects.
  kThirdParty,
  // Generated code which is not checked in.
  kGenerated,
  // Code that is generated by a macro.
  kMacro,
  // System headers (specified to Clang by -isystem).
  kSystem,
};

// Determines if a SourceLocation is considered part of first-party or
// third-party code, or is generated code, which can be used to determine how or
// which rules should be enforced.
LocationClassification ClassifySourceLocation(
    const clang::HeaderSearchOptions& search,
    const clang::SourceManager& sm,
    clang::SourceLocation loc);

}  // namespace chrome_checker

#endif  // TOOLS_CLANG_PLUGINS_UTIL_H_
